package com.sy.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sy.dto.*;
import com.sy.entity.*;
import com.sy.mapper.*;
import com.sy.service.BlogService;
import com.sy.utils.HtmlAnalysisUtils;
import com.sy.utils.HttpUtils;
import com.sy.utils.RedisClient;
import com.sy.vo.UsersBlogParams;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.sy.constant.BusinessConstant.*;

/**
 * Description:
 * date: 2021/1/15 18:57
 *
 * @author:
 * @since JDK 1.8
 */
@Service
public class BlogServiceImpl implements BlogService {
    @Autowired
    private BlogMapper blogMapper;
    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private FollowMapper followMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private CategoryCollectionMapper categoryCollectionMapper;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private HttpUtils httpUtils;
    @Autowired
    private HtmlAnalysisUtils htmlAnalysisUtils;

    @Value("${uploadDir}")
    private String uploadDir;

    @Override
    public JsonResult getNavBlogInfos(Integer id) {
        //创建博客的查询条件的对象
        BlogExample blogExample = new BlogExample();
        //按创建时间倒叙排列
        blogExample.setOrderByClause("created desc");
        //创建查询条件对象
        BlogExample.Criteria criteria = blogExample.createCriteria();
        //条件1：当前博客不是草稿
        criteria.andIsDraftEqualTo(BLOG_IS_NOT_DRAFT);
        //条件2：按分类查询
        //如果categoryId为1时，此时按推荐查找
        if(id == CATEGORY_IS_RECOMMEND){
            //点击次数
            //TODO:从redis中获取点击量
            //收藏次数
            blogExample.or().andCollectCountGreaterThanOrEqualTo(RECOMMEND_BLOG_MIN_THUMBS_COUNT);
            //点赞次数
            blogExample.or().andThumbsCountGreaterThanOrEqualTo(RECOMMEND_BLOG_MIN_COLLECT_COUNT);
        }
        //如果categoryId为2时，此时按动态查找
        else if(id == CATEGORY_IS_TRENDS){
            //获取当前登陆的用户
            User user = httpUtils.getLoginUser();
            if (null == user) {
                return httpUtils.buildFailure(404, "请先登录");
            }
            //查询当前用户关注的博主
            FollowExample followExample = new FollowExample();
            FollowExample.Criteria followCriteria = followExample.createCriteria();
            followCriteria.andFollowIdEqualTo(user.getId());
            List<Follow> follows = followMapper.selectByExample(followExample);
            //当前用户关注的博主的id集合
            List<Integer> userIds = follows.stream().map(follow -> follow.getUserId()).collect(Collectors.toList());
            //根据博主id查询博主的博客
            criteria.andUserIdIn(userIds);
        }
        //此时直接按分类id进行查找
        else{
            //先查询出该类型对应的类型集合
            CategoryCollectionExample categoryCollectionExample = new CategoryCollectionExample();
            CategoryCollectionExample.Criteria categoryCollectionCriteria = categoryCollectionExample.createCriteria();
            //通过categoryParentId查找
            categoryCollectionCriteria.andCategoryParentIdEqualTo(id);
            List<CategoryCollection> categoryCollections = categoryCollectionMapper.selectByExample(categoryCollectionExample);
            //如果该分类有博客信息
            if(CollectionUtils.isNotEmpty(categoryCollections)){
                //获得该分类对应的所有博客id的集合
                List<Integer> blogIds = categoryCollections.stream().map(CategoryCollection::getBlogId).collect(Collectors.toList());
                //设置查询条件
                criteria.andIdIn(blogIds);
            }else {
                criteria.andIdEqualTo(BLOG_ID_IS_ZERO);
            }
        }
        //分页条件
        PageHelper.startPage(1, 20);
        //按条件查询出博客
        List<Blog> blogs = blogMapper.selectByExampleWithBLOBs(blogExample);
        //按分页条件查询出的分页相关信息
        Page<Blog> page = (Page<Blog>) blogs;
        //分页页码、页数等相关信息对象
        PageInfo<Blog> pageInfo = new PageInfo<>();
        //同名属性复制
        BeanUtils.copyProperties(page,pageInfo);
        //创建返回给前端的数据对象
        PagingNavBlogInfo pagingNavBlogInfo = new PagingNavBlogInfo();
        //设置分页相关的信息
        pagingNavBlogInfo.setPageInfo(pageInfo);
        //博客信息(博客、用户、评论数)的集合
        List<NavBlogInfo> navBlogInfos = new ArrayList<>();
        //遍历博客集合
        for(Blog blog : blogs){
            //该篇博客信息对象
            NavBlogInfo navBlogInfo = new NavBlogInfo();
            //当前博客的id
            Integer blogId = blog.getId();
            //从redis中读取该篇博客的浏览数
            String clickCount = redisClient.hget(REDIS_BLOG_CLICK_HASH_KEY, String.valueOf(blogId));
            if(StringUtils.isNotBlank(clickCount)){
                //将浏览次数设置到blog中
                blog.setClickCount(Integer.parseInt(clickCount));
            }else {
                blog.setClickCount(0);
            }
            //对博客内容进行解析，只传递预览文本
            //获得html类型的文本
            String htmlContent = blog.getContent();
            //将html类型的文本解析为只包含文字内容
            List<String> strContent = htmlAnalysisUtils.getPtext(htmlContent, 1);
            //将解析后的内容设置到blog中
            blog.setContent(strContent.get(0));
            //设置该篇博客
            navBlogInfo.setBlog(blog);
            //创建当前博客的用户id
            Integer userId = blog.getUserId();
            //获得创建当前博客的用户
            User user = userMapper.selectByPrimaryKey(userId);
            //为了安全置空密码
            user.setUserPwd(null);
            //设置发表该篇博客的用户
            navBlogInfo.setUser(user);
            //查询该篇博客的评论数量
            CommentExample commentExample = new CommentExample();
            //获得comment查询条件对象
            CommentExample.Criteria commentCriteria = commentExample.createCriteria();
            //设置查询条件
            commentCriteria.andBlogIdEqualTo(blogId);
            //获得该篇博客的评论数量
            long commentCount = commentMapper.countByExample(commentExample);
            //设置该篇博客的评论数量
            navBlogInfo.setCommentCount(commentCount);
            //放入到集合
            navBlogInfos.add(navBlogInfo);
        }
        pagingNavBlogInfo.setBlogInfos(navBlogInfos);
        return httpUtils.buildSuccess(200,pagingNavBlogInfo);
    }


    /**
     *@功能：上传博客
     *
     *@Transactional:事务管理，该方法中执行了三步操作
     *1.删除本地冗余图片
     *2.将博客数据插入到博客表中
     *3.将博客的标签插入到标签表中
     */
    @Override
    @Transactional(
            //当出现指定类型异常或其子类异常的时候进行回滚，默认对所有的运行时异常进行回滚
            rollbackFor = Exception.class,
            //当出现什么异常的时候不进行回滚
            //noRollbackFor =
            //传播行为
            propagation = Propagation.REQUIRED,
            //隔离级别
            isolation = Isolation.READ_COMMITTED,
            //超时时间
            timeout = -1,
            //只读性
            readOnly = false
    )
    public JsonResult loadBlog(Blog blog, List<Category> categories) {

        JsonResult jsonResult = new JsonResult();
        CategoryCollection categoryCollection = new CategoryCollection();
        /**
         * 1.删除本地冗余图片
         * */
        removeImg(blog.getContent());

        /**
         * 2.上传博客内容到博客表中
         * */


        blog.setClickCount(0);
        blog.setThumbsCount(0);
        blog.setIsOriginal((byte)1);
        blog.setCollectCount(0);


        blogMapper.insert(blog);
        int blogId = blog.getId();
        /**
         * 3.上传博客的标签信息
         * */
        if(null != categories){
            for (Category category:categories) {

                categoryCollection.setBlogId(blogId);
                categoryCollection.setCategoryId(category.getId());
                categoryCollection.setCategoryParentId(category.getParentId());



                categoryCollectionMapper.insert(categoryCollection);

            }
        }



        return httpUtils.buildSuccess(201,blog);
    }

    @Override
    public JsonResult getDetailBlogInfo(Integer blogId) {
        //根据博客id获得该篇博客
        Blog blog = blogMapper.selectByPrimaryKey(blogId);
        //根据该篇博客的用户id获得该用户
        User user = userMapper.selectByPrimaryKey(blog.getUserId());
        //进入博客详情页需要在redis中增加浏览次数
        //以hash类型存入，在页面需要渲染blog的浏览次数的地方使用
        redisClient.hincrby(REDIS_BLOG_CLICK_HASH_KEY,String.valueOf(blogId),REDIS_CLICK_HASH_INITIAL_COUNT);
        //以hash类型存入，在页面需要渲染用户的浏览次数的地方使用
        redisClient.hincrby(REDIS_USER_CLICK_HASH_KEY,String.valueOf(user.getId()),REDIS_CLICK_HASH_INITIAL_COUNT);
        //以sorted set类型存入，在页面的用户周排行榜处使用
        redisClient.zincrby(REDIS_BLOG_USER_WEEK_RANKING_KEY,REDIS_CLICK_SORTED_SET_INITIAL_COUNT,String.valueOf(user.getId()));
        //设置周排行榜的失效时间为一周
        redisClient.expire(REDIS_BLOG_USER_WEEK_RANKING_KEY,7*24*3600);
        //以sorted set类型存入，在页面的用户总排行榜处使用
        redisClient.zincrby(REDIS_BLOG_USER_TOTAL_RANKING_KEY,REDIS_CLICK_SORTED_SET_INITIAL_COUNT,String.valueOf(user.getId()));
        //博客详情对应的对象
        DetailBlogInfo detailBlogInfo = new DetailBlogInfo();
        //从redis中获取博客的浏览量设置到blog中
        String clickCount = redisClient.hget(REDIS_BLOG_CLICK_HASH_KEY, String.valueOf(blogId));
        if(StringUtils.isNotBlank(clickCount)){
            //将浏览次数设置到blog中
            blog.setClickCount(Integer.parseInt(clickCount));
        }else {
            blog.setClickCount(0);
        }
        //将博客设置到返回给前端的对象中
        detailBlogInfo.setBlog(blog);
        //将该篇博客的作者设置到返回给前端的对象中
        detailBlogInfo.setUser(user);
        //根据博客标签获得该篇博客的文章标签
        CategoryCollectionExample categoryCollectionExample = new CategoryCollectionExample();
        CategoryCollectionExample.Criteria categoryCollectionCriteria = categoryCollectionExample.createCriteria();
        categoryCollectionCriteria.andBlogIdEqualTo(blogId);
        List<CategoryCollection> categoryCollections = categoryCollectionMapper.selectByExample(categoryCollectionExample);
        //遍历标签集合获得对应的标签id的集合
        List<Integer> categoryIds = categoryCollections.stream().map(CategoryCollection::getCategoryId).collect(Collectors.toList());
        //获得该文章对应的标签集合
        CategoryExample categoryExample = new CategoryExample();
        CategoryExample.Criteria categoryCriteria = categoryExample.createCriteria();
        //此处对集合判空防止sql语句错误
        //如果不为空则添加查询字段
        if(CollectionUtils.isNotEmpty(categoryIds)){
            categoryCriteria.andIdIn(categoryIds);
        }
        //如果为空则查询id为0的category，此时查询结果为空
        else {
            categoryCriteria.andIdEqualTo(CATEGORY_ID_IS_ZERO);
        }
        List<Category> categories = categoryMapper.selectByExample(categoryExample);
        detailBlogInfo.setCategories(categories);
        return httpUtils.buildSuccess(200,detailBlogInfo);
    }

    @Override
    public JsonResult getHotestBlogOverview(Integer userId) {
        BlogExample blogExample = new BlogExample();
        BlogExample.Criteria criteria = blogExample.createCriteria();
        criteria.andUserIdEqualTo(userId);
        //获取该用户的所有的博客
        List<Blog> blogs = blogMapper.selectByExample(blogExample);
        //获取点击次数最多的五篇博客

        return null;
    }


    /**
     *
     *@功能：删除本地冗余图片
     *
     *@入参:
     *
     *@出参：
     */
    private void removeImg(String html){
        String imgPath = "";

        //获取该用户编辑博客时保存所有图片名字
        Integer userid = httpUtils.getLoginUser().getId();
        String key = REDIS_BLOG_IMGS_CACHE+userid;

        Set<String> localImgs = redisClient.smembers(key);
        System.out.println("本地redis中存储的图片："+localImgs);

        //获取html中所有图片名
        Set<String>  htmlImgs =htmlAnalysisUtils.getImgName(html);

        //对比中转集合
        Set<String>  result = new HashSet();

        //获取两个集合的交集
        result.addAll(localImgs);

        result.retainAll(htmlImgs);

        localImgs.removeAll(result);

        //执行删除
        for (String localImg:localImgs) {
            imgPath = uploadDir + localImg;
            File file = new File(imgPath);//文件路径
            if (file.exists()){
                file.delete();
                System.err.println("检测到本地问及服务器冗余图片，执行删除："+imgPath);
            }
        }
        //清除redis中的博客图片缓存
        redisClient.del(key);


    }
    @Override
    public JsonResult getHotestBlog() {
        BlogExample blogExample = new BlogExample();
        blogExample.setOrderByClause("clickCount desc");
        List<Blog> blogs = blogMapper.selectByExample(blogExample);
        return httpUtils.buildSuccess(200, blogs);
    }

    @Override
    public JsonResult getThumbsBlog() {
        BlogExample blogExample = new BlogExample();
        blogExample.setOrderByClause("thumbsCount desc");
        List<Blog> blogs = blogMapper.selectByExample(blogExample);
        return httpUtils.buildSuccess(200, blogs);
    }

    @Override
    public JsonResult  getCollectCountBlog() {
        BlogExample blogExample = new BlogExample();
        blogExample.setOrderByClause("collectCount desc");
        List<Blog> blogs = blogMapper.selectByExample(blogExample);
        return httpUtils.buildSuccess(200, blogs);
    }



    @Override
    public JsonResult getBlogsByCategoryId(Integer categoryId) {
        CategoryCollectionExample categoryCollectionExample = new CategoryCollectionExample();
        /*blogExample.setOrderByClause("created desc");*/
        CategoryCollectionExample.Criteria criteria = categoryCollectionExample.createCriteria();
        criteria.andCategoryIdEqualTo(categoryId);
        List<CategoryCollection> categoryCollections = categoryCollectionMapper.selectByExample(categoryCollectionExample);
        List<Blog> blogs = new ArrayList<>();
        //遍历博客集合
        for (CategoryCollection categoryCollection : categoryCollections) {
            Integer blogId = categoryCollection.getBlogId();
            Blog blog = blogMapper.selectByPrimaryKey(blogId);
            blogs.add(blog);
        }
        return httpUtils.buildSuccess(200, blogs);
    }

    @Override
    public JsonResult getUserByBlogId(Integer blogId) {
        BlogExample blogExample = new BlogExample();
        blogExample.setOrderByClause("clickCount desc");
        BlogExample.Criteria criteria = blogExample.createCriteria();
        criteria.andIdEqualTo(blogId);
        List<Blog> blogs = blogMapper.selectByExample(blogExample);
        List<User> users = new ArrayList<>();
        for (Blog blog:blogs){
            Integer userId = blog.getUserId();
            User user = userMapper.selectByPrimaryKey(userId);
            users.add(user);
        }
        return httpUtils.buildSuccess(200,users);
    }
}
